#include "LogListPanel.h"
#include "spdlog/common.h"
#include "spdlog/details/console_globals.h"
#include "spdlog/details/log_msg.h"
#include "spdlog/formatter.h"
#include "spdlog/logger.h"
#include "spdlog/sinks/basic_file_sink.h"
#include "spdlog/spdlog.h"
#include "src/agvmodule_defines.h"
#include "ui_LogListPanel.h"
#include <chrono>
#include <functional>
#include <memory>

#include <mutex>
#include <qcolor.h>
#include <qcoreapplication.h>
#include <qdebug.h>
#include <qnamespace.h>
#include <qpushbutton.h>
#include <qthread.h>
#include <spdlog/sinks/callback_sink.h>
#include <spdlog/sinks/rotating_file_sink.h>
#include <spdlog/sinks/sink.h>
#include <string>
#include <vector>

#include "../CustomEvent.h"

struct LogListEventData {
  spdlog::level::level_enum level;
  std::string str;
};

using LogListEvent =
    AGVModuleAbstractEvent<LogListEventData, QT_USER_EVENT_LOGLIST>;

#include <QDebug>

LogListPanel::LogListPanel(QWidget *parent)
    : QWidget(parent), ui(new Ui::LogListPanel) {
  ui->setupUi(this);

  init_thread_handle_ = QThread::currentThreadId();

  auto callback_sink = std::make_shared<spdlog::sinks::callback_sink_st>(
      [this](const spdlog::details::log_msg &msg) {
        this->_logged_callback(msg);
      });
  callback_sink->set_level(spdlog::level::trace);

  auto default_logger = spdlog::default_logger();
  auto sinks = default_logger->sinks();
  sinks.emplace_back(callback_sink);

  auto new_logger =
      std::make_shared<spdlog::logger>("G", sinks.begin(), sinks.end());
  new_logger->set_level(spdlog::level::trace);

  default_logger->swap(*new_logger);
  default_logger->flush_on(spdlog::level::trace);

  formatter_ = std::make_shared<spdlog::pattern_formatter>();
  formatter_->set_pattern("%H:%M:%S %n [%L] %v");

  _init_pb();

  auto font = ui->listWidget->font();
  font.setPointSize(8);
  font.setFamily("Consolas");
  ui->listWidget->setFont(font);
  ui->pb_clear->setFont(font);
}

void LogListPanel::_init_pb() {
  connect(ui->pb_clear, &QPushButton::clicked, this,
          [this]() { ui->listWidget->clear(); });
}

void LogListPanel::customEvent(QEvent *e) {
  switch (e->type()) {
  default: {
    e->ignore();
    break;
  }
  case LogListEvent::type: {
    e->accept();

    auto casted_e = dynamic_cast<LogListEvent *>(e);
    if (!casted_e) {
      qWarning() << "dynamic cast LogListEvent failed";
      break;
    }

    _log_to_ui(casted_e->data().level, casted_e->data().str);
  }
  }
}

void LogListPanel::_logged_callback(const spdlog::details::log_msg &msg) {
  // auto content_str = std::string{msg.payload.begin(), msg.payload.end()};

  // auto level_strv = spdlog::level::to_string_view(msg.level);
  // auto level_str = std::string{level_strv.begin(), level_strv.end()};

  // auto time = std::chrono::time_point_cast<std::chrono::seconds>(msg.time);
  // auto time_str = EDM_FMT::format("[{:%F %T}]", time);

  // qDebug().noquote() << QString::fromStdString(time_str)
  //                    << QString::fromStdString(level_str);

  spdlog::memory_buf_t dest;
  {
    std::lock_guard guard(formatter_mutex_);
    if (!formatter_) {
        return;
    }

    formatter_->format(msg, dest);
  }
  auto str = std::string{dest.begin(), dest.end()};
  if (str.back() == '\n') {
    str.pop_back();
  }

  if (init_thread_handle_ != QThread::currentThreadId()) {
    // qDebug() << "loglist different threadid" << init_thread_handle_
    //          << QThread::currentThreadId();

    str.append(" [*]");
    QCoreApplication::postEvent(
        this, new LogListEvent(LogListEventData{msg.level, str}));
  } else {
    this->_log_to_ui(msg.level, str);
  }
}

void LogListPanel::_log_to_ui(spdlog::level::level_enum level,
                              const std::string &str) {
  ui->listWidget->addItem(QString::fromStdString(str));
  auto item = ui->listWidget->item(ui->listWidget->count() - 1);
  if (item) {
    switch (level) {
    case spdlog::level::level_enum::debug:
      item->setBackground(QColor{0, 191, 255});
      break;
    default:
    case spdlog::level::level_enum::trace:
      break;
    case spdlog::level::level_enum::info:
      item->setBackground(QColor{0, 238, 118});
      break;
    case spdlog::level::level_enum::warn:
      item->setBackground(QColor{255, 193, 37});
      break;
    case spdlog::level::level_enum::err:
    case spdlog::level::level_enum::critical:
      item->setBackground(QColor{255, 69, 0});
      item->setForeground(Qt::white);
      break;
    }
  }

  ui->listWidget->scrollToBottom();
}

LogListPanel::~LogListPanel() {
  delete ui;
  {
    std::lock_guard guard(formatter_mutex_);
    formatter_.reset();
  }
}
